package org.springblade.modules.pay.service;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.commodity.entity.Commodity;
import org.springblade.modules.order.entity.Order;
import org.springblade.modules.order.entity.OrderRefund;
import org.springblade.modules.order.service.IOrderRefundService;
import org.springblade.modules.order.service.IOrderService;
import org.springblade.modules.order.vo.OrderVO;
import org.springblade.modules.orderprocess.entity.OrderProcess;
import org.springblade.modules.orderprocess.service.IOrderProcessService;
import org.springblade.modules.pay.config.WxPayConfig;
import org.springblade.modules.pay.constant.ProcessStatus;
import org.springblade.modules.pay.constant.VerifyStatus;
import org.springblade.modules.pay.utils.HttpServletUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static com.wechat.pay.java.core.http.Constant.*;


/**
 * wx支付服务
 *
 * @author zhz
 * @date 2024/09/07
 */
@Slf4j
@Service
@AllArgsConstructor
public class WxPayServiceImpl implements WxPayService {

	private final WxPayConfig wxPayConfig;

	private final RSAAutoCertificateConfig rsaAutoCertificateConfig;

	private final IOrderService orderService;

	private final IOrderProcessService orderProcessService;

	private final IOrderRefundService	orderRefundService;


	/***
	 * 预支付订单
	 * @param req
	 * @return
	 */
	@Override
	public PrepayWithRequestPaymentResponse createOrder(Order req) {
		req.setPayStatus(2);
		//创建初始化订单
		OrderProcess orderProcess = new OrderProcess();
		BladeUser user = AuthUtil.getUser();
		if (user != null) {
			req.setPhone(user.getAccount());
		}
		boolean save = orderService.save(req);
		orderProcess.setOrderId(req.getId());
		orderProcess.setFillTime(new Date());
		orderProcess.setAction(ProcessStatus.CREATED.getDescription());
		orderProcessService.save(orderProcess);
		PrepayWithRequestPaymentResponse response = new PrepayWithRequestPaymentResponse();
		if (save) {
			String orderNo = req.getOutTradeNo();
			List<Commodity> commodityList = req.getCommodityList();
			List<BigDecimal> bigDecimals = commodityList.stream().map(Commodity::getPrice).toList();
			double totalAmount = bigDecimals.stream().mapToDouble(BigDecimal::doubleValue).sum() * 100;
//			Integer totalAmount = bigDecimals.stream().mapToInt(Doubl::valueOf).sum() * 100;
			//请求微信支付相关配置
			// 不填默认为RSA
			JsapiServiceExtension service =
				new JsapiServiceExtension.Builder()
					.config(rsaAutoCertificateConfig)
					.signType("RSA")
					.build();
			try {
				PrepayRequest request = new PrepayRequest();
				request.setAppid(wxPayConfig.getAppId());
				request.setMchid(wxPayConfig.getMerchantId());
				request.setDescription("用户下单");
				request.setOutTradeNo(orderNo);
				request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
				Amount amount = new Amount();
				amount.setTotal((int) totalAmount);
				request.setAmount(amount);
				Payer payer = new Payer();
				payer.setOpenid(req.getWxOpenId());
				request.setPayer(payer);
				log.info("请求预支付下单，请求参数：{}", JSONObject.toJSONString(request));
				// 调用预下单接口
				response = service.prepayWithRequestPayment(request);
				log.info("订单【{}】发起预支付成功，返回信息：{}", orderNo, response);
				// 发送HTTP请求失败
			} catch (HttpException e) {
				log.error("微信下单发送HTTP请求失败，错误信息：{}", e.getHttpRequest());
			} catch (ServiceException e) {
				// 服务返回状态小于200或大于等于300，例如500
				log.error("微信下单服务状态错误，错误信息：{}", e.getErrorMessage());
			} catch (MalformedMessageException e) {
				// 服务返回成功，返回体类型不合法，或者解析返回体失败
				log.error("服务返回成功，返回体类型不合法，或者解析返回体失败，错误信息：{}", e.getMessage());

			}
			return response;
		}
		return response;
	}

	/***
	 * 微信支付回调通知
	 * @param request
	 * @return
	 * @throws IOException
	 */
	@Transactional
	@Override
	public synchronized boolean payNotify(HttpServletRequest request) throws Exception {
		log.info("------收到支付通知------");
		// 请求头Wechatpay-Signature
		String signature = request.getHeader("Wechatpay-Signature");
		// 请求头Wechatpay-nonce
		String nonce = request.getHeader("Wechatpay-Nonce");
		// 请求头Wechatpay-Timestamp
		String timestamp = request.getHeader("Wechatpay-Timestamp");
		// 微信支付证书序列号
		String serial = request.getHeader("Wechatpay-Serial");
		// 签名方式
		String signType = request.getHeader("Wechatpay-Signature-Type");
		// 构造 RequestParam
		RequestParam requestParam = new RequestParam.Builder()
			.serialNumber(serial)
			.nonce(nonce)
			.signature(signature)
			.timestamp(timestamp)
			.signType(signType)
			.body(HttpServletUtils.getRequestBody(request))
			.build();

		// 初始化 NotificationParser
		NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
		// 以支付通知回调为例，验签、解密并转换成 Transaction
		log.info("验签参数：{}", requestParam);
		Transaction transaction = parser.parse(requestParam, Transaction.class);
		log.info("验签成功！-支付回调结果：{}", transaction.toString());
		String outTradeNo = transaction.getOutTradeNo();
		return queryOrderByOrderNo(outTradeNo);
	}


	/***
	 * 根据商户订单号查询订单 outTradeNo
	 * @param outTradeNo
	 * @return
	 */
	@Transactional
	public boolean queryOrderByOrderNo(String outTradeNo) {
		QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
		queryRequest.setMchid(wxPayConfig.getMerchantId());
		queryRequest.setOutTradeNo(outTradeNo);
		try {
			JsapiServiceExtension service =
				new JsapiServiceExtension.Builder()
					.config(rsaAutoCertificateConfig)
					.signType("RSA") // 不填默认为RSA
					.build();
			com.wechat.pay.java.service.payments.model.Transaction result = service.queryOrderByOutTradeNo(queryRequest);
			//支付成功
			com.wechat.pay.java.service.payments.model.Transaction.TradeStateEnum tradeState = result.getTradeState();
			if (com.wechat.pay.java.service.payments.model.Transaction.TradeStateEnum.SUCCESS == tradeState) {
				log.info("内部订单号【{}】,微信支付订单号【{}】支付成功", result.getOutTradeNo(), result.getTransactionId());
				//主动查询
				Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOutTradeNo, outTradeNo));
				if (Func.isNotEmpty(order)) {
					if (order.getPayStatus() != 1) {
						order.setPayStatus(1);
						order.setTransactionId(result.getTransactionId());
						order.setPayTime(result.getSuccessTime());
						order.setVerifyStatus(VerifyStatus.CREATED.getStatus());
						orderService.updateById(order);
						// todo 同时处理支付记录
						OrderProcess orderProcess = new OrderProcess();
						orderProcess.setOrderId(order.getId());
						orderProcess.setFillTime(new Date());
						orderProcess.setAction(ProcessStatus.PAID.getDescription());
						orderProcessService.save(orderProcess);
					}
				}
			} else {
				log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
			}
		} catch (ServiceException e) {
			log.error("订单查询失败，返回码：{},返回信息：{}", e.getErrorCode(), e.getErrorMessage());
		}
		return true;
	}


	/**
	 * 微信小程序退款
	 *
	 * @param orderRefund 退款原因
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean refund(OrderRefund orderRefund) {
		synchronized (this) {
			Order order = orderService.selectById(orderRefund.getOrderId());
			if (Func.isEmpty(order)) {
				throw new org.springblade.core.log.exception.ServiceException("无订单信息");
			}
			if (order.getPayStatus() == 2){
				throw new org.springblade.core.log.exception.ServiceException("订单未支付无法退款");
			}
			List<OrderRefund> list = orderRefundService.list(new LambdaQueryWrapper<OrderRefund>().eq(OrderRefund::getOrderId, order.getId()));
			if (Func.isNotEmpty(list)){
				BigDecimal reduce = list.stream().map(OrderRefund::getRefundPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
				if (orderRefund.getRefundPrice().compareTo(reduce)>0){
					throw new org.springblade.core.log.exception.ServiceException("累计退款金额不能大于订单金额");
				}
			}
			BigDecimal reduce = order.getCommodityList().stream().map(Commodity::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
			log.info("退款订单总金额：{}",reduce);
			if (orderRefund.getRefundPrice().compareTo(reduce) > 0 ){
				throw new org.springblade.core.log.exception.ServiceException("退款金额不能大于订单金额");
			}
			// 使用自动更新平台证书的RSA配置
			// 一个商户号只能初始化一个配置，否则会因为重复的下载任务报错
			// 构建退款service
			RefundService service = new RefundService.Builder().config(rsaAutoCertificateConfig).build();
			//构建退款请求
			CreateRequest request = new CreateRequest();
			// request.setXxx(val)设置所需参数，具体参数可见Request定义
			//构建订单金额信息
			AmountReq amountReq = new AmountReq();
			//退款金额
			amountReq.setRefund((long) (orderRefund.getRefundPrice().doubleValue() * 100));
			//原订单金额
			amountReq.setTotal((long) (order.getCommodityList().stream().map(Commodity::getPrice).toList().stream().mapToDouble(BigDecimal::doubleValue).sum() * 100));
			//货币类型(默认人民币)
			amountReq.setCurrency("CNY");
			request.setAmount(amountReq);
			request.setOutTradeNo(order.getOutTradeNo());
			request.setReason("退款");
			//商户退款单号
			request.setOutRefundNo(String.valueOf(orderRefund.getRefundNum()));
			//退款通知回调地址，开设可不设，我是没有设置的
			//如果要设置就在配置加上对应的参数和参数值即可
			request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());

			// 调用微信sdk退款接口
			Refund refund = service.create(request);
			log.info("请求退款返回：" + refund);
			//接收退款返回参数
			//  Status status = refund.getStatus();
			orderRefund.setRefundNum(refund.getRefundId());
			orderRefund.setOutTradeNo(order.getOutTradeNo());
			orderRefund.setStatus(refund.getStatus().ordinal());
			orderRefund.setRefundNum(refund.getOutRefundNo());
			orderRefund.setStatus(2);
			orderRefundService.save(orderRefund);
			OrderProcess orderProcess = new OrderProcess();
			orderProcess.setOrderId(order.getId());
			orderProcess.setFillTime(new Date());
			orderProcess.setAction(ProcessStatus.REFUND_REQUESTED.getDescription());
			orderProcessService.save(orderProcess);
			order.setPayStatus(3);
			orderService.updateById(order);
		}
		return false;
	}


	/**
	 * 退款回调
	 *
	 * @param request
	 * @return
	 */
	@Override
	public boolean refundNotify(HttpServletRequest request) {

			//读取请求体的信息
		String s1 = getString(request);
		String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
			String nonce = request.getHeader(WECHAT_PAY_NONCE);
			String signType = request.getHeader("Wechatpay-Signature-Type");
			String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
			String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
			// 初始化 NotificationParser
			NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
			RequestParam requestParam = new RequestParam.Builder()
				.serialNumber(serialNo)
				.nonce(nonce)
				.signature(signature)
				.timestamp(timestamp)
				.signType(signType)
				.body(s1)
				.build();
			RefundNotification parse = parser.parse(requestParam, RefundNotification.class);
			System.out.println("parse = " + parse);
			if (Status.SUCCESS.equals(parse.getRefundStatus())) {
				//你的业务代码，根据请求返回状态修改对应订单状态
				String outTradeNo = parse.getOutTradeNo();
				String outRefundNo = parse.getOutRefundNo();
				OrderRefund orderRefund = orderRefundService.getOne(new LambdaQueryWrapper<OrderRefund>().eq(OrderRefund::getRefundNum, outRefundNo).eq(OrderRefund::getOutTradeNo, outTradeNo));
				orderRefund.setStatus(1);
				OrderProcess orderProcess = new OrderProcess();
				orderProcess.setOrderId(orderRefund.getOrderId());
				orderProcess.setFillTime(new Date());
				orderProcess.setAction(ProcessStatus.REFUNDED.getDescription());
				orderProcessService.save(orderProcess);
				return orderRefundService.updateById(orderRefund);
			}
		return false;
	}

	private static @NotNull String getString(HttpServletRequest request) {
		ServletInputStream inputStream = null;
		try {
			inputStream = request.getInputStream();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		StringBuilder stringBuffer = new StringBuilder();
		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
		String s;
		//读取回调请求体
		while (true) {
			try {
				if ((s = bufferedReader.readLine()) == null) {
					break;
				}
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
			stringBuffer.append(s);
		}
		return stringBuffer.toString();
	}
}
